/**
 * @class 渐进弥散器组件
 * @author DuskyHuang 圣子
 * @description 使用这个组件可以模拟无穷无尽的空间粒子、纹理。
 * @description 我们使用这个来近似模拟未知的超远外太空的星际分布。
*/

import { _decorator, Component, instantiate, Material, MeshRenderer, Node, ParticleSystem, Prefab, Vec4 } from 'cc';
import { DEG_PERIGON, LAYER_MAIN_3D } from 'const';
import { createNodeAt } from 'helpers';
import { cosmos } from 'proxies';
const { ccclass, property } = _decorator;

@ccclass('cocosmos.GradientPervasion')
export class GradientPervasion extends Component {

    @property(Prefab) public SuperClusters: Prefab = null;
    @property(Prefab) public Stars: Prefab = null;
    @property(Prefab) public Galaxies: Prefab = null;
    @property(Material) public TestMat: Material = null;

    protected onLoad(): void { this._init(); }

    protected update(): void {
        this._updateSuperClusters();
        // this._updateStarClusters();
    }

    private _updateSuperClusters(): void {
        if (cosmos.super) {
            if (this._clusters.children.length <= 0) this._createSuperClusters();
            const { pow10 } = cosmos;
            const [FLOOR, CEIL] = CLUSTERS_RANGE;
            const a = pow10 - FLOOR, b = CEIL - pow10, c = a / (a + b);
            if (b <= 0) {
                const s = Math.pow(10, b);
                this._clusters.setScale(s, s, s);
            } else {
                this._gradient(this._clusters);
                this._clusters.setScale(1, 1, 1);
            }
            clusters.x = clusters.y = c + 1;
            // clusters.x = clusters.y = 1;                     // Tiling
            clusters.z = Math.clamp01(a) * 0.7;                 // Strength
            clusters.w = Math.lerp(0.5, 0.1, Math.clamp01(c));  // Fading
            this._c_mat.passes[0].setUniform(this._c_handle, clusters);
            this._clusters.active = true;
        } else this._clusters.active = false;
    }

    private _updateStarClusters(): void {
        if (cosmos.local) {
            if (this._stars.children.length <= 0) this._createStarClusters();
            this._gradient(this._stars, 1);
            this._stars.active = true;
        } else this._stars.active = false;
    }


    private _gradient(container: Node, offset: num.posInt = 0): void {
        const { pow10 } = cosmos;
        let pow2 = Math.log2(Math.pow(10, pow10));
        container.children.forEach((child, idx) => {
            let p = idx - pow2;
            p %= 5;
            const s = Math.pow(2, p + offset);
            child.setScale(s, s, s);
        });
    }

    private _createSuperClusters(): void {
        const rot = DEG_PERIGON / GRADIENT_NUM
        for (let i = 0; i < GRADIENT_NUM; i++) {
            const cluster = instantiate(this.SuperClusters);
            cluster.setRotationFromEuler(0, i * rot, i * rot / 2);
            cluster.parent = this._clusters;
            if (i === 0) {
                this._c_mat = cluster.getComponent(MeshRenderer)!.sharedMaterial;
                this._c_handle = this._c_mat.passes[0]?.getHandle('clusters');
            }
        }
    }

    private _createStarClusters(): void {
        for (let i = 0; i < GRADIENT_NUM; i++) {
            const stars = instantiate(this.Stars);
            stars.parent = this._stars;
            stars.getComponent(ParticleSystem).setMaterial(this.TestMat, 0);
        }
    }

    private _init(): void {
        this._stars = createNodeAt("Pervasive Stars", LAYER_MAIN_3D, this.node);
        this._galaxies = createNodeAt("Pervasive Galaxies", LAYER_MAIN_3D, this.node);
        this._clusters = createNodeAt("Pervasive Clusters", LAYER_MAIN_3D, this.node);
    }

    private _stars: Node = null;
    private _galaxies: Node = null;
    private _clusters: Node = null;
    private _c_mat: Material = null;
    private _c_handle: number;
}

const CLUSTERS_RANGE = [8.5, 10];
const GRADIENT_NUM = 5;
const clusters = new Vec4;